<?php 
if(!defined('_JEXEC')) {
   define('_JEXEC', 1);
   define('JPATH_BASE', OC::$SERVERROOT . '/../web/');
}

require_once JPATH_BASE.'/includes/defines.php';
require_once JPATH_BASE.'/includes/framework.php';

JLoader::import('joomla.user.user', JPATH_LIBRARIES);
JLoader::import('joomla.utilities.date', JPATH_LIBRARIES);
JLoader::import('joomla.filesystem.folder', JPATH_LIBRARIES);
JLoader::import('joomla.filesystem.file', JPATH_LIBRARIES);

JLoader::register("JobHelper", JPATH_SITE.'/components/com_puser/jobhelper.php');
JLoader::register("DelayTaskJob", JPATH_SITE.'/components/com_jobmail/helpers/delaytask.php');
JLoader::register("PCompanyHelper", OC::$SERVERROOT."/apps/company/lib/helper.php");
JLoader::register("CacheHelper", JPATH_SITE.'/components/com_jobs/cache/helper.php');

require_once(JPATH_SITE.'/components/com_puser/jobutil.php');

require_once(OC::$SERVERROOT.'/apps/joomla/joomla.php');
require_once(OC::$SERVERROOT.'/apps/joomla/util.php');
require_once(OC::$SERVERROOT.'/apps/joomla/pagination.php');
require_once(OC::$SERVERROOT.'/apps/joomla/storage.php');

/*
 * handle session and state registration 
 */

class PSession extends JSession { 
   public static function getInstance($handler, $options)
   {
      if (!is_object(self::$instance))
      {
         self::$instance = new PSession($handler, $options);
      }

      return self::$instance;
   }

   public static function getFormToken($forceNew = false)
   {
      return isset($_SESSION['requesttoken']) ? 
             $_SESSION['requesttoken']: OC_Util::generate_random_bytes(20); 
   }

   public static function checkToken($method = 'post') {
      $app = PFactory::getApplication();
      $token = self::getFormToken();
      if ($app->input->$method->get('requesttoken', '', 'alnum') != $token && 
          $app->input->server->get('HTTP_REQUESTTOKEN', '', 'alnum') != $token)
         return false; 

      return true; 
   }

}

class PFactory extends JFactory  {
   public static function init() {
      if(self::$application != null)
         return; 

      /* init joomla application */
      self::$application = new PApplication(); 

      /* init language */
      $lang = self::getLanguage();
      $lang->load("lib_joomla", JPATH_SITE, "zh-CN");
      $lang->load("", JPATH_SITE, "zh-CN");

      /* can it login */
      self::tryRememberLogin(); 
   }

   public static function getApplication($id = null, array $config = array(), $prefix = 'J') {
      if(!self::$application)
         exit("System initialization error"); 

      return self::$application;
   }

   /**
    * Get a session object.
    *
    * Returns the global {@link JSession} object, only creating it if it doesn't already exist.
    *
    * @param   array  $options  An array containing session options
    *
    * @return  JSession object
    *
    * @see     JSession
    * @since   11.1
    */
   public static function getSession(array $options = array())
   {
      if (!self::$session)
      {
         self::$session = self::createSession($options);
      }

      return self::$session;
   }

   /**
    * Create a session object
    *
    * @param   array  $options  An array containing session options
    *
    * @return  JSession object
    *
    * @since   11.1
    */
   protected static function createSession(array $options = array())
   {
      // Get the editor configuration setting
      $conf = self::getConfig();
      $handler = $conf->get('session_handler', 'none');

      // Config time is in minutes
      $options['expire'] = ($conf->get('lifetime')) ? $conf->get('lifetime') * 60 : 900;

      $session = PSession::getInstance($handler, $options);
      if ($session->getState() == 'expired')
      {
         $session->restart();
      }

      return $session;
   }

   public static function getUser($id = null) {
      $instance = self::getSession()->get('user');
      if (is_null($id)) {
         if (!($instance instanceof JUser))
         {
            $instance = JUser::getInstance();
         }
      }
      elseif ($instance->id != $id) {
         $instance = JUser::getInstance($id);
      }

      return $instance;
   }

   public static function isJobseekerUser($id = null) {
       $user = self::getUser($id);
       if(!$user->get("guest") && isset($user->cb_usertype) && $user->cb_usertype == 'jobseeker'){
           return true;
       }   
       return false;
   }   
   
   public static function isMasterUser() {
     if(OC_User::getUser() === false)
         return false;

      $user = self::getUser();
      if($user->get("guest") || $user->get('id')<=0)
         return false; 

      // cannot be jobseeker 
      if(isset($user->cb_usertype) && $user->cb_usertype == 'jobseeker')
         return false; 

      return true; 
   }

   public static function isAdminUser() {
      if(OC_User::getUser() === false)
         return false; 

      $user = self::getUser(); 
      if(!$user->get("guest") || $user->get("id")<=0)
         return true; 

      /* check if session cached */
      $session = self::getSession();
      if($session->has('isadmin'))
         return $session->get('isadmin'); 

      /* a subaccount, let see if he is in sub groups */
      $db = self::getDbo();
      $query = "SELECT COUNT(*) AS num FROM `*PREFIX*group_user_custom` AS a " . 
               " LEFT JOIN *PREFIX*groups_custom AS b ON b.gid=a.gid" . 
               " WHERE a.uid = " . $db->quote(OC_User::getUser()) . " AND b.isadmin= 1" . 
               " GROUP BY a.uid"; 

      $db->setQuery(PUtil::replaceOCPrefix($query));
      $isadmin = $db->loadResult();

      $session->set("isadmin", $isadmin > 0 ? true : false); 
         
      return $session->get('isadmin');
   }

   public static function getMasterUser() {
      
      $userid = OC_User::getUser(); 
      if($userid === false)
         return self::getUser();

      // for sub accounts, they are not stored in joomla, id is 0 
      $user = self::getUser();
      if(!$user->get("guest") && $user->get("id")>0)
         return $user;

      /* check if session cached */
      $session = self::getSession();
      if($session->has('masteruser'))
         return $session->get('masteruser'); 

      $db = self::getDbo(); 
      $query = "SELECT ue.owner, c.avatar FROM *PREFIX*users_extended AS ue
                LEFT JOIN #__comprofiler AS c ON c.user_id=ue.owner WHERE uid = " . $db->quote($userid); 
      $db->setQuery(PUtil::replaceOCPrefix($query)); 
      $masteruser = $db->loadObject();
      if(!$masteruser)
         return self::getUser();
      
      $avatar = $masteruser->avatar;
      $masteruser = self::getUser($masteruser->owner);
      $masteruser->avatar = $avatar;
      $session->set('masteruser', $masteruser);
      return $session->get('masteruser'); 
   }

   /**
    * Try to use remember cookie login 
    */
   private static function tryRememberLogin() {
      if(OC_User::isLoggedIn())
         return; 

      // load user backend so authorization can be correctly set 
      OC_User::useBackend(new OC_User_Database());
      OC_Group::useBackend(new OC_Group_Database());
      OC_App::loadApp('user_joomla');

      $hash = PApplication::getHash('PLOGIN_REMEMBER');
      if ($str = JRequest::getString($hash, '', 'cookie', JREQUEST_ALLOWRAW | JREQUEST_NOTRIM)) {
         // Create the encryption key, apply extra hardening using the user agent string.
         // Since we're decoding, no UA validity check is required.
         $privateKey = PApplication::getHash(@$_SERVER['HTTP_USER_AGENT']);

         $key = new JCryptKey('simple', $privateKey, $privateKey);
         $crypt = new JCrypt(new JCryptCipherSimple, $key);
         $str = $crypt->decrypt($str);
         $cookieData = @unserialize($str);
         // Deserialized cookie could be any object structure, so make sure the
         // credentials are well structured and only have user and password.
         $filter = JFilterInput::getInstance();
         $goodCookie = true;
         if (isset($cookieData['username']) && is_string($cookieData['username'])) {
            $username = $filter -> clean($cookieData['username'], 'username');
         } 
         else {
            $goodCookie = false;
         }
         if (isset($cookieData['password']) && is_string($cookieData['password'])) {
            $password = $filter -> clean($cookieData['password'], 'string');
         }
         else {
            $goodCookie = false;
         }

         if (!$goodCookie || !OC_User::login($username, $password)) {
            $config = PFactory::getConfig();
            $cookie_domain = $config->get('cookie_domain', '');
            $cookie_path = $config->get('cookie_path', '/');
            // Clear the remember me cookie
            setcookie(PApplication::getHash('PLOGIN_REMEMBER'), false, time() - 86400, $cookie_path, $cookie_domain );
         }

         $user = PFactory::getUser();
         // user chould be not valid as if the logged user is sub-user of the company user
         if(!$user->get("guest")) {
            if($user->get("id") > 0)
               JobHelper::setLastLogonDate($user);
            else 
               PCompanyHelper::setSubUserLastLogonDate($user->get("username")); 
         }

         $return = self::$application->getUserState("return");
         if(!empty($return)) {
            self::$application->setUserState("return", "");
            OC_Response::redirect($return);
            exit;
         }
         // fall through  
      }
   }

}

class PApplication extends JApplication {
   /**
    * Currently active template
    * @var object
    */
   private $template = null;

   public function __construct($config = array()) {
      $this->_name = "cloud"; 
      $this->_clientId = 0; 

      // $config['session'] = false; 
      parent::__construct($config);

      $session = PFactory::getSession();
      /* a hook class to automatically load all Joomla framework when OC initializes. don't use it for other purpose */
      // Disabled as we load apps when session is initialized 
      // if(!$session->has('joomla'))
      //   $session->set('joomla', new OCA\joomla\Joomla);

      if ($session->isNew()){
         $session->set('registry', new JRegistry('session'));
         $session->set('user', new JUser);
      }

   }

   protected function _createSession($name) {
      $options = array();
      $options['name'] = $name;
      switch ($this->_clientId)
      {
         case 0:
            if ($this->getCfg('force_ssl') == 2)
            {
               $options['force_ssl'] = true;
            }
            break;

         case 1:
            if ($this->getCfg('force_ssl') >= 1)
            {
               $options['force_ssl'] = true;
            }
            break;
      }

      $session = PFactory::getSession($options);
      $session->initialise($this->input, $this->dispatcher);
      $session->start();

      $db = PFactory::getDbo();

      $config = PFactory::getConfig();
      $handler = $config->get('session_handler', null);

      // adjust frequence to check database 
      $freq = 2; 
      if($handler != 'database')
         $freq = 60; 

      // Remove expired sessions from the database.
      $time = time();
      if ($time % $freq == 0) {
         // The modulus introduces a little entropy, making the flushing less accurate
         // but fires the query less than half the time.
         $query = $db->getQuery(true);
         $query->delete($query->qn('#__session'))
            ->where($query->qn('time') . ' < ' . $query->q((int) ($time - $session->getExpire())));

         $db->setQuery($query);
         $db->execute();
      }

      // Check to see the the session already exists.
      if (($handler != 'database' && ($time % $freq ==0 || $session->isNew()))
         || ($handler == 'database' && $session->isNew())) {
            $this->checkSession();
      }

      return $session; 
   }

   public function checkSession() {
      $db = PFactory::getDbo();
      $session = PFactory::getSession();
      $user = PFactory::getUser();
      if(OC_User::getUser() !== false) {
         $user->set('username', OC_User::getUser());
      }

      $query = $db->getQuery(true);
      $query->select($query->qn('session_id'))
         ->from($query->qn('#__session'))
         ->where($query->qn('session_id') . ' = ' . $query->q($session->getId()));

      $db->setQuery($query, 0, 1);

      $exists = $db->loadResult();

      // If the session record doesn't exist initialise it.
      if (!$exists) {
         $query->clear();
         if ($session->isNew()) {
            $query->insert($query->qn('#__session'))
               ->columns($query->qn('session_id') . ', ' . $query->qn('client_id') . ', ' . $query->qn('time'))
               ->values($query->q($session->getId()) . ', ' . (int) $this->getClientId() . ', ' . $query->q((int) time()));
            $db->setQuery($query);
         }
         else {
            $query->insert($query->qn('#__session'))
               ->columns(
                  $query->qn('session_id') . ', ' . $query->qn('client_id') . ', ' . $query->qn('guest') . ', ' .
                  $query->qn('time') . ', ' . $query->qn('userid') . ', ' . $query->qn('username')
               )
               ->values(
                  $query->q($session->getId()) . ', ' . (int) $this->getClientId() . ', ' . (int) $user->get('guest') . ', ' .
                  $query->q((int) $session->get('session.timer.start')) . ', ' . (int) $user->get('id') . ', ' . $query->q($user->get('username'))
               );

            $db->setQuery($query);
         }

         // If the insert failed, exit the application.
         try {
            $db->execute();
         } catch(Exception $e) { 
            echoDbgLog($e);
            jexit("Fail to initialize session");
         } 
            
      }
   }

   public function setUserSession($user) {
      $session = PFactory::getSession();
      $session->clear("user");
      $session->set("user", $user);

      // Update the user related fields for the Joomla sessions table.
      $user->set('guest', 0);

      // Even when use memcached as session storage, we still need to keep some session related information into db 
      // for other purpose(login status; subaccount or master account based on userid, etc)
      // but the session write will be dramatically reduced with enabling memcached, so to lower db replication burden
      $db = PFactory::getDbo();
      $db->setQuery(
          'UPDATE '.$db->quoteName('#__session') .
          ' SET '.$db->quoteName('guest').' = '.$db->quote($user->get('guest')).',' .
          '  '.$db->quoteName('username').' = '.$db->quote($user->get('username')).',' .
          '  '.$db->quoteName('userid').' = '.(int) $user->get('id') .
          ' WHERE '.$db->quoteName('session_id').' = '.$db->quote($session->getId())
      );
      $db->execute();
   }

   /**
    * update session time field. This is only needed if the session handler is not database, 
    * the application needs to find out which user is active now based on session id 
    */
   public function updateSessionTime() {
      $config = PFactory::getConfig();
      $handler = $config->get('session_handler', null);
      if ($handler == 'database')
         return; 

      $db = PFactory::getDbo();
      $session = PFactory::getSession();

      $query = $db->getQuery(true);
      $query->update($db->quoteName('#__session'))
      ->set($db->quoteName('time') . ' = ' . $db->quote((int) time()))
      ->where($db->quoteName('session_id') . ' = ' . $db->quote($session->getId()));

      // Try to update the session data in the database table.
      $db->setQuery($query);
      $db->execute(); 
   }

   public function getUserState($key, $default = null) {
      $session = PFactory::getSession();
      $registry = $session->get('registry');

      if (!is_null($registry)) {
         return $registry->get($key, $default);
      }

      return $default;
   }

   /**
    * Sets the value of a user state variable.
    *
    * @param   string  $key    The path of the state.
    * @param   string  $value  The value of the variable.
    *
    * @return  mixed  The previous state, if one existed.
    *
    * @since   11.1
    */
   public function setUserState($key, $value) {
      $session = PFactory::getSession();
      $registry = $session->get('registry');

      if (!is_null($registry)) {
         return $registry->set($key, $value);
      }

      return null;
   }

   /**
    * Get the template
    *
    * @return  string The template name
    * @since 1.0
    */
   public function getTemplate($params = false) {
      if (is_object($this->template)) {
         return $this->template->template;
      }

      // Load styles
      $db = PFactory::getDbo();
      $query = $db->getQuery(true);
      $query->select('id, home, template, s.params');
      $query->from('#__template_styles as s');
      $query->where('s.client_id = 0');
      $query->where('s.home = 1');
      $query->where('e.enabled = 1');
      $query->leftJoin('#__extensions as e ON e.element=s.template AND e.type='.$db->quote('template').' AND e.client_id=s.client_id');
      $db->setQuery($query);
      $templates = $db->loadObjectList();
      if(!$templates)
         return 'jobs'; 

      /* just take first one */
      $template = $templates[0];
      $registry = new JRegistry;
      $registry->loadString($template->params);
      $template->params = $registry;

      // Cache the result
      $this->template = $template;
      return $template->template;
   }
}
